package xfuzzy.xfghl.model;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;

import xfuzzy.lang.Operatorset;
import xfuzzy.xfds.XfdsDataSet;
import xfuzzy.xfds.XfdsParser;
import xfuzzy.xfghl.output.GAOutput;
import xfuzzy.xfghl.view.XfghlProperties;
import xfuzzy.xfhl.model.hierStruct.XfhlHierStruct;
import xfuzzy.xfhl.model.spec.XfhlOperSet;

/**
 * Clase que representa la configuracion de la herramienta Xfghl
 * @author Alberto David Fernandez Fernandez
 */

public class XfghlConfig {

	/**
	 * Fichero en el que se encuentra el conjunto de datos a utilizar
	 */
	private File datasetfile;

	/**
	 * Conjunto de datos a utilizar
	 */
	private XfdsDataSet dataset;

	/**
	 * Fichero de salida
	 */
	private String outPutFile;

	/**
	 * Estilo comun a todas las variables de entrada
	 */
	private int styleInPut;

	/**
	 * Numero de funciones de pertenencia de la variable de entrada
	 */
	private int mfs;

	/**
	 * El algoritmo de ajuste elegido
	 */
	private int algorithm;

	/**
	 * Parametro Initial Update del algoritmo
	 */
	private double iniUpd;

	/**
	 * Parametro Increase Factor del algoritmo
	 */
	private double incFact;

	/**
	 * Parametro Decrease Factor del algoritmo
	 */
	private double decFact;

	/**
	 * Operador AND
	 */
	private int andOperator;

	/**
	 * Operator Defuz
	 */
	private int DefuzOperator;

	/**
	 * Numero de entradas de nuestro modulo
	 */
	private int numVarEnt;

	/**
	 * Nombre del sistema
	 */
	private String nameSystem;

	/**
	 * Numero de iteraciones del algoritmo de ajuste
	 */
	private int numIterAjust;

	/**
	 * Numero de thread
	 */
	private int numThread;

	/**
	 * Variable que nos dice el numero de procesadores disponibles
	 */
	private int numProc;

	/**
	 * Numero de estructuras
	 */
	private int numStruct;
	
	/**
	 * Operador
	 * */

	private Operatorset operSet;
	
	/**
	 * Tamaño de la poblacion
	 * */
	private int populationSize;
	
	/**
	 * Semilla para la generacion de la poblacion
	 * */
	private long popopulationSeed;
	
	/**
	 * Probabilidad de mutacion
	 * */
	private double mutationProbability;
	
	/**
	 * Semilla para la mutacion
	 * */
	private long mutationSeed;
	
	/**
	 * Tamaño del torneo de seleccion
	 * */
	private int tournamentSize;
	
	/**
	 * Semilla para la seleccion
	 * */
	private long selectionSeed;
	
	/**
	 * Factor de escala para el crowding deterministico en el reemplazo
	 * */
	private double scalingFactor;
	
	/**
	 * Semilla para el reemplazo
	 * */
	private long replacementSeed;
	
	/**
	 * Condicion de parada seleccionada
	 * */
	private int stopConditionGA;
	
	/**
	 * Valor para la condicion de parada
	 * */
	private int valueStopConditionGA;
	
	/**
	 * Semilla para el cruce
	 * */
	private long crossoverSeed;
	
	/**
	 * Ruta para la mejor estructura jerarquica
	 * */
	private String outputBest;
	
	/**
	 * Ruta para el fichero log
	 * */
	private String logFile;
	
	/**
	 * Semilla para la reparacion de individuos
	 * */
	private long reparationSeed;

	/**
	 * Constructor por defecto
	 */
	public XfghlConfig() {

	}

	/**
	 * Constructor de la clase
	 * @param nameSystem
	 *            Nombre que va a llevar el sistema final
	 * @param numIterAjus
	 *            Numero de iteraciones que va a utilizar el algoritmo
	 * @param numThread
	 *            Numero de hebras que va a utilizar el programa
	 * @param file
	 *            Archivo que contiene el data set
	 * @param iniUpd
	 *            Parametro Initial Update del algoritmo RPROP
	 * @param incFact
	 *            Parametro Increase Factor del algortimo RPROP
	 * @param decFact
	 *            Parametro Decrease Factor del algortimo RPROP
	 * @param andOperator
	 *            Operador AND
	 * @param defuzOperator
	 *            Operador de defuzificacion
	 * @param styleInPut
	 *            Estilo de las variables de entrada
	 * @param mfs
	 *            Numero de variables de funciones de las variables de entrada
	 * @param alg
	 *            Algoritmo de ajuste elegido
	 * @param mutProb
	 * 			  Probabilidad de mutacion
	 * @param popSize
	 * 			  Volumen de poblacion
	 * @param scaleFact
	 * 			  Factor de escala en el reemplazo
	 * @param stopSelec
	 * 			  Condicion de parada
	 * @param valueStop
	 * 			  Valor de la condicion de parada          
	 */
	
	public XfghlConfig(String nameSystem, int numIterAjus, int numThread, File file, double iniUpd, double incFact,
			double decFact, int andOperator, int defuzOperator, int styleInPut, int mfs, int alg, double mutProb, 
			int popSize, int tournamentSize, double scaFact, int stopSelec, int valueStop) {
		
		this(nameSystem, numIterAjus, numThread, file, iniUpd, incFact, decFact, andOperator, defuzOperator, styleInPut, mfs, alg);
		this.mutationProbability = mutProb;
		this.populationSize = popSize;
		this.tournamentSize = tournamentSize;
		this.scalingFactor = scaFact;
		this.stopConditionGA = stopSelec;
		this.valueStopConditionGA = valueStop;
		this.crossoverSeed = 0;
		this.mutationSeed = 0;
		this.selectionSeed = 0;
		this.replacementSeed = 0;
		this.popopulationSeed = 0;
		this.outputBest = "";
		this.logFile = "";
		this.reparationSeed = 0;
	}
	
	/**
	 * Constructor de la clase
	 * @param nameSystem Nombre que va a llevar el sistema final
	 * @param numIterAjus Numero de iteraciones que va a utilizar el algoritmo
	 * @param numThread Numero de hebras que va a utilizar el programa
	 * @param file Archivo que contiene el data set
	 * @param iniUpd Parametro Initial Update del algoritmo RPROP
	 * @param incFact Parametro Increase Factor del algortimo RPROP
	 * @param decFact Parametro Decrease Factor del algortimo RPROP
	 * @param andOperator Operador AND
	 * @param defuzOperator Operador de defuzificacion
	 * @param styleInput Estilo de las variables de entrada
	 * @param mfs Numero de variables de funciones de las variables de entrada
	 * @param alg Algoritmo de ajuste elegido
	 * */

	public XfghlConfig(String nameSystem, int numIterAjus, int numThread, File file, double iniUpd, double incFact,
			double decFact, int andOperator, int defuzOperator, int styleInPut, int mfs, int alg) {
		
		setDataSet(file);

		this.nameSystem = nameSystem;
		this.numVarEnt = this.dataset.getNumberOfInputs();
		this.numIterAjust = numIterAjus;
		this.numThread = numThread;
		this.numStruct = XfhlHierStruct.numEstructuras(this.numVarEnt);
		this.algorithm = alg;
		this.iniUpd = iniUpd;
		this.incFact = incFact;
		this.decFact = decFact;
		this.andOperator = andOperator;
		this.DefuzOperator = defuzOperator;
		this.operSet = XfhlOperSet.create("OperSet", andOperator, defuzOperator);
		this.styleInPut = styleInPut;
		this.mfs = mfs;

		OperatingSystemMXBean mxbean;
		mxbean = ManagementFactory.getOperatingSystemMXBean();
		this.numProc = mxbean.getAvailableProcessors();
	}

	public boolean setDataSet(File file) {
		
		try {
			this.datasetfile = file;
			FileInputStream fis = new FileInputStream(datasetfile);
			XfdsParser parser = new XfdsParser(fis);
			XfdsDataSet ds = parser.parse();
			if (ds == null)
				return false;
			this.dataset = ds;
			return true;
		} 
		catch (Exception ex) {
			return false;
		}
	}

	/**
	 * Obtiene el fichero del conjunto de datos
	 * 
	 * @return
	 */
	public File getDataSetFile() {
		return this.datasetfile;
	}

	public int getnumIterAjust() {
		return this.numIterAjust;
	}

	public int getnumVarEnt() {
		return this.numVarEnt;
	}

	public int getNumThread() {
		if (this.numThread == -1)
			return this.numProc;
		else
			return this.numThread;
	}

	public int getValueThread() {
		return this.numThread;
	}

	public void setNumThread(int numThread) {
		this.numThread = numThread;
	}

	public int getNumStruc() {
		return this.numStruct;
	}

	public void setNameSystem(String name) {
		this.nameSystem = name;
	}

	public String getNameSystem() {
		return this.nameSystem;
	}

	public Operatorset getOperSet() {
		return this.operSet;
	}

	public XfdsDataSet getDataSet() {
		return this.dataset;
	}

	public int getStileInput() {
		return this.styleInPut;
	}

	public int getMfs() {
		return this.mfs;
	}

	public String getOutputFile() {
		return this.outPutFile;
	}

	public double getIniUpd() {
		return this.iniUpd;

	}

	public double getIncFact() {
		return this.incFact;
	}

	public double getDecFact() {
		return this.decFact;
	}

	public int getAndOperator() {
		return this.andOperator;
	}

	public int getDefuzOperator() {
		return this.DefuzOperator;
	}

	public int getAlgorithm() {
		return this.algorithm;
	}

	public int getPopulationSize() {
		return populationSize;
	}

	public void setPopulationSize(int populationSize) {
		this.populationSize = populationSize;
	}

	public double getMutationProbability() {
		return mutationProbability;
	}

	public void setMutationProbability(double mutationProbability) {
		this.mutationProbability = mutationProbability;
	}

	public int getTournamentSize() {
		return tournamentSize;
	}

	public void setTournamentSize(int tournamentSize) {
		this.tournamentSize = tournamentSize;
	}

	public double getScalingFactor() {
		return scalingFactor;
	}

	public void setScalingFactor(double scalingFactor) {
		this.scalingFactor = scalingFactor;
	}

	public int getStopConditionGA() {
		return stopConditionGA;
	}

	public void setStopConditionGA(int stopConditionGA) {
		this.stopConditionGA = stopConditionGA;
	}

	public int getValueStopConditionGA() {
		return valueStopConditionGA;
	}

	public void setValueStopConditionGA(int valueStopConditionGA) {
		this.valueStopConditionGA = valueStopConditionGA;
	}

	public long getMutationSeed() {
		return mutationSeed;
	}

	public void setMutationSeed(long mutationSeed) {
		this.mutationSeed = mutationSeed;
	}

	public long getSelectionSeed() {
		return selectionSeed;
	}

	public void setSelectionSeed(long selectionSeed) {
		this.selectionSeed = selectionSeed;
	}

	public long getReplacementSeed() {
		return replacementSeed;
	}

	public void setReplacementSeed(long replacementSeed) {
		this.replacementSeed = replacementSeed;
	}

	public long getPopopulationSeed() {
		return popopulationSeed;
	}

	public void setPopopulationSeed(long popopulationSeed) {
		this.popopulationSeed = popopulationSeed;
	}

	public long getCrossoverSeed() {
		return crossoverSeed;
	}

	public void setCrossoverSeed(long crossoverSeed) {
		this.crossoverSeed = crossoverSeed;
	}

	public String getOutputBest() {
		return outputBest;
	}

	public void setOutputBest(String outputBest) {
		this.outputBest = outputBest;
	}

	public String getLogFile() {
		return logFile;
	}

	public void setLogFile(String logFile) {
		this.logFile = logFile;
	}

	public long getReparationSeed() {
		return reparationSeed;
	}

	public void setReparationSeed(long reparationSeed) {
		this.reparationSeed = reparationSeed;
	}
	
	/**
	 * Metodo que genera una cadena con formato XML de la configuracion de la herramienta
	 * @return String Cadena con formato XML
	 * */

	public String toXML() {
		
		String eol = System.getProperty("line.separator", "\n");
		String code = "<xfuzzy>" + eol;
		code += "\t<xfghl system=\"" + this.nameSystem + "\">" + eol;

		code += "\t\t<dataset file=\"" + this.datasetfile.getAbsolutePath() + "\"></dataset>" + eol + eol;

		code += "\t\t<!-- NumThreads: " + eol + "\t\t\tdefault -> Number of available processors in the system. " + eol
				+ "\t\t\tNumber of processors --> " + eol;
		String threads;
		if (this.numThread == -1)
			threads = "default";
		else
			threads = "" + this.numThread;
		code += "\t\t<threads numbers=\"" + threads + "\"></threads>" + eol + eol;

		code += "\t\t<!-- ALGORITHM: " + eol + "\t\t\t0: RProp " + eol + "\t\t\t1: Mardquardt Levenberg --> " + eol;

		code += "\t\t<learning algorithm =\"" + this.algorithm + "\"></learning>" + eol + eol;

		code += "\t\t<!-- STYLE: " + eol + "\t\t\t1: Free Triangles " + eol + "\t\t\t2: Free Shouldered Triangles "
				+ eol + "\t\t\t3: Free Gaussians " + eol + "\t\t\t4: Triangles Family " + eol
				+ "\t\t\t5: Shouldered-Triangular Family " + eol + "\t\t\t6: B-Splines Family --> " + eol;
		code += "\t\t<input_type style=\"" + this.styleInPut + "\" num_funct=\"" + this.mfs + "\"></input_type>" + eol
				+ eol;

		code += "\t\t<alg_param ini=\"" + this.iniUpd + "\" inc_fact=\"" + this.incFact + "\" dec_fact=\""
				+ this.decFact + "\"></alg_param>" + eol + eol;

		code += "\t\t<end_condition interations=\"" + this.numIterAjust + "\"></end_condition>" + eol + eol;

		code += "\t\t<!-- AND: " + eol + "\t\t\t0: Function MIN " + eol + "\t\t\t1: Function PROD " + eol
				+ "\t\t DEFUZ: " + eol + "\t\t\t0: Fuzzy Mean " + eol + "\t\t\t1: Weigthed Fuzzy Mean -->" + eol;

		code += "\t\t<operartor_set and=\"" + this.andOperator + "\" defuz=\"" + this.DefuzOperator
				+ "\"></operartor_set>" + eol + eol;
		
		code += "\t\t<mutation probability=\"" + this.mutationProbability + "\" seed=\"" + this.mutationSeed +
				"\"></mutation>" + eol + eol;
		
		code += "\t\t<crossover seed=\"" + this.crossoverSeed + "\"></crossover>" + eol + eol;
		
		code += "\t\t<population size=\"" + this.populationSize + "\" seed=\"" + this.popopulationSeed +
				"\"></population>" + eol + eol;
		
		code += "\t\t<selection tournamente_size=\"" + this.tournamentSize + "\" seed=\"" + this.selectionSeed + 
				"\"></selection>" + eol + eol;
		
		code += "\t\t<replacement scaling_factor=\"" + this.scalingFactor + "\" seed=\"" + this.replacementSeed + 
				"\"></replacement>" + eol + eol;
		
		code += "\t\t<stopGA condition=\"" + this.stopConditionGA + "\" value=\"" + this.valueStopConditionGA + "\"></stopGA>" + eol;
		
		code += "\t\t<output log=\"" + this.logFile + "\" best=\"" + this.outputBest + "\"></output>" + eol + eol;
		
		code += "\t\t<reparation seed=\"" + this.reparationSeed + "\"></reparation>" + eol + eol;
		
		code += "\t</xfghl>" + eol;
		code += "</xfuzzy>";

		return code;
	}

	/**
	 * Guarda la configuracion en un fichero xml
	 * @param file
	 *            El archivo donde vamos a añadir la configuracion
	 * @return boolean True si se ha guardado, False en caso contrario
	 */
	public boolean save(File file) {
		
		String code = toXML();
		byte buf[] = code.getBytes();

		try {
			OutputStream stream = new FileOutputStream(file, true);
			stream.write(buf);
			stream.close();
		} 
		catch (IOException e) {
			return false;
		}
		return true;
	}
	
	/**
	 * Metodo que escribe en el fichero log la configuracion de la herramienta
	 * */

	public void logProperties()  {
		
		GAOutput.writeln("PROPIEDADES");
		GAOutput.writeln("Nombre del sistema: " + this.nameSystem, this.logFile);
		GAOutput.writeln("Dataset: " + this.datasetfile.getAbsolutePath(), this.logFile);
		if (this.styleInPut == xfuzzy.xfhl.model.spec.XfhlInputType.B_SPLINES_FAMILY)
			GAOutput.writeln("Estilo de entrada: " + XfghlProperties.bSplinesFamilyString, this.logFile);
		else if (this.styleInPut == xfuzzy.xfhl.model.spec.XfhlInputType.FREE_GAUSSIANS)
			GAOutput.writeln("Estilo de entrada: " + XfghlProperties.freeGaussiansString, this.logFile);
		else if (this.styleInPut == xfuzzy.xfhl.model.spec.XfhlInputType.FREE_SHOULDERED_TRIANGLES)
			GAOutput.writeln("Estilo de entrada: " + XfghlProperties.freeShoulderedTrianglesString, this.logFile);
		else if (this.styleInPut == xfuzzy.xfhl.model.spec.XfhlInputType.FREE_TRIANGLES)
			GAOutput.writeln("Estilo de entrada: " + XfghlProperties.freeTrianglesString, this.logFile);
		else if (this.styleInPut == xfuzzy.xfhl.model.spec.XfhlInputType.SHOULDERED_TRIANGLES_FAMILY)
			GAOutput.writeln("Estilo de entrada: " + XfghlProperties.shoulderedTrianglesFamilyString, this.logFile);
		else 
			GAOutput.writeln("Estilo de entrada: " + XfghlProperties.triangularFamilyString, this.logFile);
		
		GAOutput.writeln("Funciones de pertenencia: " + this.mfs, this.logFile);
		if (this.algorithm == XfghlProperties.RProbAlgorithm)
			GAOutput.writeln("Algoritmo: " + XfghlProperties.RProbAlgorithmString, this.logFile);
		else 
			GAOutput.writeln("Algoritmo: " + XfghlProperties.MardquardtLevenbergAlgorithmString, this.logFile);
		
		GAOutput.writeln("Initial Update algoritmo: " + this.iniUpd, this.logFile);
		GAOutput.writeln("Increase Factor algoritmo: " + this.incFact, this.logFile);
		GAOutput.writeln("Decrease Factor algoritmo: " + this.decFact, this.logFile);
		
		if (this.andOperator == xfuzzy.xfhl.model.spec.XfhlOperSet.AndOperatorMin)
			GAOutput.writeln("And operator: " + XfghlProperties.AndOperatorMinString, this.logFile);
		else
			GAOutput.writeln("And operator: " + XfghlProperties.AndOperatorProdString, this.logFile);
		
		if (this.DefuzOperator == xfuzzy.xfhl.model.spec.XfhlOperSet.DefuzOperatorFuzzyMean)
			GAOutput.writeln("Defuz operator: " + XfghlProperties.DefuzOperatorFuzzyMeanString, this.logFile);
		else
			GAOutput.writeln("Defuz operator: " + xfuzzy.xfhl.model.spec.XfhlOperSet.DefuzOperatorWeightedFuzzyMean, this.logFile);
		
		GAOutput.writeln("Numero entradas modulo: " + this.numVarEnt, this.logFile);
		GAOutput.writeln("Numero de estructuras: " + this.numStruct, this.logFile);
		GAOutput.writeln("Numero iteraciones algoritmo de ajuste: " + this.numIterAjust, this.logFile);
		GAOutput.writeln("Numero de hilos: " + this.numThread, this.logFile);

		GAOutput.writeln("Semilla mutacion: " + this.mutationSeed, this.logFile);
		GAOutput.writeln("Probabilidad mutacion: " + this.mutationProbability, this.logFile);
		GAOutput.writeln("Semilla cruce: " + this.crossoverSeed, this.logFile);
		GAOutput.writeln("Semilla poblacion: " + this.popopulationSeed, this.logFile);
		GAOutput.writeln("Individuos poblacion: " + this.populationSize, this.logFile);
		GAOutput.writeln("Individuos seleccion torneo: " + this.tournamentSize, this.logFile);
		GAOutput.writeln("Semilla seleccion torneo: " + this.selectionSeed, this.logFile);
		GAOutput.writeln("Semilla reemplazo: " + this.replacementSeed, this.logFile);
		GAOutput.writeln("Factor de escala reemplazo: " + this.scalingFactor, this.logFile);
		if (this.stopConditionGA == XfghlProperties.stopConditionEvaluationsValue)
			GAOutput.writeln("Condicion de parada: " + XfghlProperties.stopConditionEvaluationsString, this.logFile);
		else if (this.stopConditionGA == XfghlProperties.stopConditionIterationsValue)
			GAOutput.writeln("Condicion de parada: " + XfghlProperties.stopConditionIterationsString, this.logFile);
		else
			GAOutput.writeln("Condicion de parada: " + XfghlProperties.stopConditionNoImprovementsString, this.logFile);
		GAOutput.writeln("Valor de la parada: " + this.valueStopConditionGA, this.logFile);
		GAOutput.writeln("Semilla reparacion: " + this.reparationSeed, this.logFile);
	}
}
